{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to Automate SQL Database Queries with AI\n",
    "\n",
    "In this tutorial, we will learn how to automate SQL database queries with the SQL Database Agent. You will learn how to:\n",
    "\n",
    "- Connect to a SQL database\n",
    "- Have AI write SQL queries for you from natural language\n",
    "- Automate the SQL query execution\n",
    "- Retrieve the results\n",
    "\n",
    "\n",
    "\n",
    "### Want To Become A Full-Stack Generative AI Data Scientist?\n",
    "\n",
    "![Generative AI Data Scientist](../img/become_a_generative_ai_data_scientist.jpg)\n",
    "\n",
    "I teach Generative AI Data Science to help you build AI-powered data science apps. [**Register for my next Generative AI for Data Scientists workshop here.**](https://learn.business-science.io/ai-register)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "bat"
    }
   },
   "source": [
    "# Table of Contents\n",
    "\n",
    "1. [Introduction](#introduction)\n",
    "2. [Load Libraries](#load-libraries)\n",
    "3. [Setup AI and Logging](#setup-ai-and-logging)\n",
    "4. [Connect to a SQL Database](#connect-to-a-sql-database)\n",
    "5. [Create The Agent](#create-the-agent)\n",
    "6. [Run the Agent](#run-the-agent)\n",
    "7. [Response](#response)\n",
    "    1. [SQL Query Code](#sql-query-code)\n",
    "    2. [Pandas Data Frame From SQL Query](#pandas-data-frame-from-sql-query)\n",
    "    3. [Python Pipeline Function](#python-pipeline-function)\n",
    "    4. [Storage Location](#storage-location)\n",
    "8. [Free Generative AI Data Science Workshop](#free-generative-ai-data-science-workshop)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# * Libraries\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "import pandas as pd\n",
    "import sqlalchemy as sql\n",
    "\n",
    "import os\n",
    "import yaml\n",
    "from pprint import pprint\n",
    "\n",
    "from ai_data_science_team.agents import SQLDatabaseAgent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup AI and Logging\n",
    "\n",
    "This section of code sets up the LLM inputs and the logging information. Logging is used to store AI-generated code and files during the AI Data Science Teams processing of files. \n",
    "\n",
    "*Important Note:* This example uses OpenAI's API. But any LLM can be used such as Anthropic or local LLMs with Ollama."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x7f88f09364a0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x7f88d05fc0a0>, root_client=<openai.OpenAI object at 0x7f88f0a5d630>, root_async_client=<openai.AsyncOpenAI object at 0x7f890090dae0>, model_name='gpt-4o-mini', model_kwargs={}, openai_api_key=SecretStr('**********'))"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# * Setup\n",
    "\n",
    "MODEL    = \"gpt-4o-mini\"\n",
    "LOG      = True\n",
    "LOG_PATH = os.path.join(os.getcwd(), \"logs/\")\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = yaml.safe_load(open('../credentials.yml'))['openai']\n",
    "\n",
    "llm = ChatOpenAI(model = MODEL)\n",
    "\n",
    "llm\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Connect to a SQL Database\n",
    "\n",
    "Next, let's connect to the leads data from a SQL database. We will need to use a `sqlalchemy` connection to use the SQL Database Agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<sqlalchemy.engine.base.Connection at 0x7f88c048b4c0>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_engine = sql.create_engine(\"sqlite:///data/northwind.db\")\n",
    "\n",
    "conn = sql_engine.connect()\n",
    "\n",
    "conn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create The Agent\n",
    "\n",
    "Run this code to create the agent with `SQLDatabaseAgent()`.\n",
    "\n",
    "The only required parameters are `model` and `connection`. \n",
    "\n",
    "- `model` is the LLM model that you want to use.\n",
    "- `connection` is the sqlalchemy connection to the SQL database.\n",
    "\n",
    "Other parameters that I've included in this demo are:\n",
    "\n",
    "- `n_samples`: The number of samples for each table x column to share with the LLM. Reduce this number for large databases to prevent LLM Max Token errors.\n",
    "- `log`: Set up logging the SQL DB agent's pipeline function\n",
    "- `log_path`: The directory to save the SQL DB agent's pipeline function\n",
    "- `bypass_explain_code`: Dynamically bypass the explain code step in the LangGraph DAG. This is useful to speed up response time when you don't need to see the explain code.\n",
    "- `bypass_recommended_steps`: Dynamically bypass the recommended steps in the LangGraph DAG. This is useful to speed up response time when you don't need to see the recommended steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<ai_data_science_team.agents.sql_database_agent.SQLDatabaseAgent object at 0x7f88d0844a30>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Make the agent\n",
    "sql_agent = SQLDatabaseAgent(\n",
    "    model = llm, \n",
    "    connection=conn, \n",
    "    n_samples=1, # Needed for large databases to avoid token limits\n",
    "    log=LOG, \n",
    "    log_path=LOG_PATH,\n",
    "    bypass_explain_code=True,\n",
    "    bypass_recommended_steps=True,\n",
    ")\n",
    "\n",
    "sql_agent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the Agent"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The main inputs to the SQL DB Agent are:\n",
    "\n",
    "- **user_instructions**: What actions you'd like to take on the SQL database query. \n",
    "- **max_retries**: Used to limit the number of attempts to fix the SQL and Python code generated by the agent. Set this to 3 to limit to 3 attempts. \n",
    "- **retry_count**: Set this to 0. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---SQL DATABASE AGENT----\n",
      "    * CREATE SQL QUERY CODE\n",
      "    * CREATE PYTHON FUNCTION TO RUN SQL CODE\n",
      "      File saved to: /Users/mdancho/Desktop/course_code/ai-data-science-team/logs/sql_database.py\n",
      "    * EXECUTING AGENT CODE ON SQL CONNECTION\n"
     ]
    }
   ],
   "source": [
    "\n",
    "sql_agent.invoke_agent(\n",
    "    user_instructions=\"What are the sales for each product?\",\n",
    "    max_retries=3,\n",
    "    retry_count=0,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Response\n",
    "\n",
    "The response produced contains everything we need to understand the data cleaning decisions made and get the cleaned dataset. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['messages',\n",
       " 'user_instructions',\n",
       " 'data_sql',\n",
       " 'all_sql_database_summary',\n",
       " 'sql_query_code',\n",
       " 'sql_database_function',\n",
       " 'sql_database_function_path',\n",
       " 'sql_database_function_name',\n",
       " 'sql_database_error',\n",
       " 'max_retries',\n",
       " 'retry_count']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = sql_agent.get_response()\n",
    "\n",
    "list(response.keys())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### SQL Query Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```sql\n",
       "SELECT \n",
       "    p.ProductID,\n",
       "    p.ProductName,\n",
       "    SUM(od.Quantity) AS TotalSales\n",
       "FROM \n",
       "    Products p\n",
       "JOIN \n",
       "    \"Order Details\" od ON p.ProductID = od.ProductID\n",
       "GROUP BY \n",
       "    p.ProductID, p.ProductName\n",
       "ORDER BY \n",
       "    p.ProductID;\n",
       "```"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_agent.get_sql_query_code(markdown=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Pandas Data Frame From SQL Query"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>ProductID</th>\n",
       "      <th>ProductName</th>\n",
       "      <th>TotalSales</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>Chai</td>\n",
       "      <td>201905</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>Chang</td>\n",
       "      <td>201802</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>Aniseed Syrup</td>\n",
       "      <td>202186</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>Chef Anton's Cajun Seasoning</td>\n",
       "      <td>198726</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>Chef Anton's Gumbo Mix</td>\n",
       "      <td>199627</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>72</th>\n",
       "      <td>73</td>\n",
       "      <td>Röd Kaviar</td>\n",
       "      <td>199042</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>73</th>\n",
       "      <td>74</td>\n",
       "      <td>Longlife Tofu</td>\n",
       "      <td>202113</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>74</th>\n",
       "      <td>75</td>\n",
       "      <td>Rhönbräu Klosterbier</td>\n",
       "      <td>201021</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75</th>\n",
       "      <td>76</td>\n",
       "      <td>Lakkalikööri</td>\n",
       "      <td>201478</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>76</th>\n",
       "      <td>77</td>\n",
       "      <td>Original Frankfurter grüne Soße</td>\n",
       "      <td>201852</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>77 rows × 3 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "    ProductID                      ProductName  TotalSales\n",
       "0           1                             Chai      201905\n",
       "1           2                            Chang      201802\n",
       "2           3                    Aniseed Syrup      202186\n",
       "3           4     Chef Anton's Cajun Seasoning      198726\n",
       "4           5           Chef Anton's Gumbo Mix      199627\n",
       "..        ...                              ...         ...\n",
       "72         73                       Röd Kaviar      199042\n",
       "73         74                    Longlife Tofu      202113\n",
       "74         75             Rhönbräu Klosterbier      201021\n",
       "75         76                     Lakkalikööri      201478\n",
       "76         77  Original Frankfurter grüne Soße      201852\n",
       "\n",
       "[77 rows x 3 columns]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_agent.get_data_sql()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Python Pipeline Function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "```python\n",
       "# Disclaimer: This function was generated by AI. Please review before using.\n",
       "# Agent Name: sql_database_agent\n",
       "# Time Created: 2025-01-10 07:53:58\n",
       "\n",
       "\n",
       "def sql_database_pipeline(connection):\n",
       "    import pandas as pd\n",
       "    import sqlalchemy as sql\n",
       "    \n",
       "    # Create a connection if needed\n",
       "    is_engine = isinstance(connection, sql.engine.base.Engine)\n",
       "    conn = connection.connect() if is_engine else connection\n",
       "\n",
       "    sql_query = '''\n",
       "    SELECT \n",
       "    p.ProductID,\n",
       "    p.ProductName,\n",
       "    SUM(od.Quantity) AS TotalSales\n",
       "FROM \n",
       "    Products p\n",
       "JOIN \n",
       "    \"Order Details\" od ON p.ProductID = od.ProductID\n",
       "GROUP BY \n",
       "    p.ProductID, p.ProductName\n",
       "ORDER BY \n",
       "    p.ProductID;\n",
       "    '''\n",
       "    \n",
       "    return pd.read_sql(sql_query, connection)\n",
       "        \n",
       "```"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sql_agent.get_sql_database_function(markdown=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Want To Become A Full-Stack Generative AI Data Scientist?\n",
    "\n",
    "![Generative AI Data Scientist](../img/become_a_generative_ai_data_scientist.jpg)\n",
    "\n",
    "I teach Generative AI Data Science to help you build AI-powered data science apps. [**Register for my next Generative AI for Data Scientists workshop here.**](https://learn.business-science.io/ai-register)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ds4b_301p_dev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
